Title: Death-defying stat boosts fix
Author: Assassin
Version: Version 0.20
Applies to: Secret of Evermore -- US version, English, French, German, and Spanish
            European versions
Tested on: US version, and English European version (the German, French, and Spanish
           European versions are identical to it in the involved functions)

Contents:

        NOTE: These patches are for HEADERED ROMs only!  That means a filesize of
        3146240 bytes.  Don't let any zealots convince you that headers are dangerous!

        StaDie-U.ips = the SoE (USA) patch
        StaDie-E.ips = the SoE (Europe - English, French, German, and Spanish) patch

        NStaDieU.ips = the SoE (USA) anti-patch
        NStaDieE.ips = the SoE (Europe - English, French, German, and Spanish) anti-patch

        The "Anti-patches" effectively remove this patch.

        readme.txt = this file

        status-remove-death-orig.txt = commented original code (SoE US)
        status-remove-death-fix.asm = commented fixed code (SoE US).  Also includes how
                                      the SoE European patches' offsets and values differ
                                      from the SoE US version's.
        various-status-handlers.txt = commented US code of status handlers for Regrowth,
                                      Slow Burn, Corrosion, Plague, and Poison.  Just
                                      here for reference; none are buggy.  My "Overeager
                                      Speed timer" fix has the Atlas, Defend, and Speed
                                      handlers, if you're curious.

ROM Addresses: SoE US - D1/BB2D thru D1/BB7A
               SoE European (English, German, French, and Spanish) - D1/BA1C thru D1/BA69
  (NOTE: The game actually runs the Bank Dn code in Bank 9n.)

Functions added (SoE USA and SoE European): None
Additional Square functions used (SoE USA and SoE European): None

Urgency: Medium high.  The bug can have a notable effect on character stats in either
         direction, particularly if you stack it.  It's also possible to underflow with it,
         and wind up with a game-breakingly high Attack or Defense stat (or a high Hit
         Rate % or Evade % stat with seemingly random results).  Fortunately, it is
         reversible by use of another bug; see the FAQ section.  The bug's effects are
         accentuated by levelling up Atlas, Defend, or Speed.  Because the Boy's death
         normally reloads the game, one is less likely to stumble onto the bug with him than
         one is with the Dog.  Nonetheless, it is still ripe for abuse by those who know
         about it.

============================================================================================

The latest versions of all my patches can be found at this site:
http://assassin17.brinkster.net/

and archived as of early October 2015:
http://web.archive.org/web/*/http://home.comcast.net/~assassin17/

============================================================================================

TABLE OF CONTENTS

0. Description
1. Cause
2. More Info
3. For Testers
4. FAQ
5. Revision History
6. Credits
Unnumbered: Copyright notice

____________________________________________________________________________________________

0. DESCRIPTION
____________________________________________________________________________________________

When a character is killed, the game removes their statuses, of which they can
have up to four at a time.  A "cleanup" function is generally also called for
each status.  The cleanup entails zeroing its timer(s), clearing flags it had
set (e.g. invincibility), and zeroing any boost the status designated for (a)
relevant statistic(s), after deducting it from the overall boost(s) a character
has to the statistic(s) due to alchemy/item statuses.

However, because a pointer isn't altered between function calls, the game
always subtracts whatever remains in the "boost" variable of the character's
first status slot.  Thus, statistics that were modified by other statuses can
wrongly wind up different than their pre-alchemy values.

Generally, the subtracted value will be a zero, provided that the status in
Slot 1 gives no boost and doesn't use this variable for anything else (e.g.
damage/healing timing), or it calls a cleanup function upon death that zeroes
out the variable.  Such cases result in any statistic elevation caused by
former Statuses 2-4 simply lingering, while other cases can cause stranger
results.  For Poison, Corrosion, and Slow Burn, which give no boost, the value
subtracted is the number of frames since the last damage interval.  This is
often large enough to push a statistic into negative territory after death,
making it behave like a huge positive number.  For Plague, it's an unknown
value (or 5 if done from an enemy Special, like Mad Monk's), that's used as a
damage base.

The Dog is more readily affected by the bug, as his fall doesn't make the game
reload, while the Boy will need Pixie Dust or Regenerate in effect (which is a
status of its own).

This patch varies the status slot pointer to properly undo statistic boosts
upon dying.

Phil Collins once sang, "They say you can't take it with you when you go, and
I believe them".  Now this game finally believes them too, Phil.

____________________________________________________________________________________________

1. CAUSE
____________________________________________________________________________________________

It was probably just an oversight by Square.  They do get the status slot
pointer right when removing a status due to another one replacing it (but they
botch something else, which a forthcoming patch will deal with).  They also get
it right when calling the timer handler functions each frame.  Inn visits,
(Miracle) Cure, and Essence apparently forgo the pointer and cleanup functions,
and wipe the slot data of "bad" statuses directly.

Only 3 of the 16 possible character statuses (of 19 overall) having boosts,
as well as death being one of the ways where removing a status doesn't give a
"[Status name] has worn off" message, could have made the mistake less likely
to be detected in playtesting.

____________________________________________________________________________________________

2. MORE INFO
____________________________________________________________________________________________

Each of the four character status slots has three 16-bit words:

- Status ID (FFFFh = none, Bit 15: 1 = most recently given, see list of IDs below)
- Main Timer (ascending from 0, frame-based)
- Boost provided to statistic(s), OR Time since/until last/next damage/healing interval

Note that since Plague doesn't auto-expire, it has Time since last interval where the Main
Timer would go, and an somewhat unknown, damage base value in the third (Time interval)
slot.

The various flags associated with statuses (e.g. Barrier's invincibility) are not kept in
these slots, nor are the overall boosts a character has to statistics due to alchemy/item
statuses.  That's why clearing out the slots' data isn't enough to undo them, and
specialized cleanup functions (passed proper pointers) are needed.

Status ID list:
00h = Atlas, 08h = Aura (Horace),
10h = Barrier, 18h = Defend,
20h = Energize, 28h = Force Field,
30h = Reflect, 38h = Shield (Camellia),
40h = Regrowth, 48h = Speed,
50h = Regenerate (Horace) and Pixie Dust, 58h = Stop * ,
60h = Confound (Horace), 68h = Disrupt (Sidney) * ,
70h = Slow Burn, 78h = Corrosion,
80h = Hypnotize (Camellia) *, 88h = Plague (Camellia),
90h = Poison, 98h = Wings helper status
FFFFh = Nothing
* Boy and Dog don't receive

____________________________________________________________________________________________

3. FOR TESTERS
____________________________________________________________________________________________

The essence of the fix is simple, but I did overhaul Function 91/BB27 pretty good,
converting it to use a loop and a stack variable.  So it's possible I messed something up.
Any testing is appreciated.  Also, because the Spanish, German, and French European versions
are identical to the English European in the areas altered, I've only tested on the last of
those four.

Please provide feedback on Slick Productions:
http://slickproductions.org/

or on Mnrogar's Den:
http://mnrogar.slickproductions.org/

or PM assassin17 on the GameFAQs message boards:
http://www.gamefaqs.com/

Thanks!

____________________________________________________________________________________________

4. FAQ
____________________________________________________________________________________________

Q: Can the effects of this bug be stacked?

A: Yup.  Repeat execution of the bug to further boost or decrease your desired stat.  But
   note that if you did a one-shot underflow (e.g. from Poison as the first status), there's
   no point to repeating.


Q: What value does Regrowth put in the "boost" variable?  And why didn't you list it as
   one of the statuses that can cause an underflow, alongside the periodic damagers?

A: The number of frames until the next healing interval.  The reason Regrowth didn't make
   the list is that unlike those other statuses, its "clean up" function is called upon
   removal when dying.  This means the value is zeroed, and you'll only get the plainer
   variety of the bug.


Q: So how do I reverse this mess?

A: The simplest way is to avoid saving, then to reset the game.

   A less intuitive way is to not give yourself any new statuses (and let any on the
   character who didn't perish expire naturally, or Cure them), save your game, then reset
   and reload it.  The statistic(s) should be normal.  This works because the "Unsaved stat
   boosts" bug in saving/loading zeroes out the cumulative "alchemy/item stat boosts"
   variable instead of retaining it.

  Q2: What if i'm playing on the ROM, I fixed that other bug, but not this one, and I saved
      my game?  Then what?

  A2: Well, then, congratulations; you've succeeded at shooting yourself in the foot in an
      especially pointless way.  You could repeat this Death-defying Stat Boosts bug until
      the stat boost value wraps back to where it should be.  That's far easier to do if
      the boost was too low.  If it was too high, you'll either need to repeat the bug
      hundreds or thousands of times (not practical), or make clever use of a timed status
      like Poison in Slot 1 by watching its number of frames since the last damage interval
      variable (a debugging emulator like Snes9x Geiger is very helpful) to plan when you
      die.

      Or remove all statuses (through auto-expiration or curing), use a PAR code to set the
      relevant "overall boost to statistic" variable to zero, save your game, turn off the
      PAR code, then reset and reload.  http://datacrystal.romhacking.net/ has the RAM
      addresses for all of the variables mentioned.

      ...Or just temporarily remove the other patch, then remove all statuses, save, reload,
      and re-apply it.  This and the PAR code are a bit simpler than the first paragraph,
      unless you're actually using my patch on a repro cart, as opposed to just making me
      jump through hoops.


Q: It's odd that some statuses don't call their "cleanup" functions upon being removed when
   a character is killed.  Why not make them do so, and thus eliminate the underflow variety
   of this bug?

A: It's more direct to simply eliminate all forms of the bug -- underflow or lingering
   boost alike -- which is what this patch does.  Also, Plague doesn't have a cleanup
   function, because the cleanups are located in the status timer handler code, and
   Plague never expires on its own.  That means I'd have to write one for it, which would
   admittedly require only a few instructions.

   Anyway, I do agree that it's odd.  It's understandable that Inns, (Miracle) Cure, and
   Essence would remove only bad statuses, but death should really wipe the whole slate
   clean.  And indeed it does remove every status, yet only calls cleanup functions for
   some.  I'm not entirely sure on the logic behind not giving ailments meaningful entries
   in the D1/AE33 pointer table.


Q: I performed the underflow variety of the bug with Speed, and my Evade % and Hit Rate %
   appear wicked high, yet I'm not dominating.  What's up?

A: The short answer is that it's essentially pointless to try and underflow these two stats.
   You're better off using the boosting variety of the bug to increase the stats, and
   repeating it as needed.  Read on for more information regarding the underflow.

   When calculating your character's total Evade or Hit Rate, the game will cap it at 99 if
   it's 100 to 32866.  If it's 32867 to 65535, the game will leave it as-is, since it never
   anticipated the sum being so high.

   As for determining chance to hit, there isn't just a simple formula, but data tables that
   are indexed using target Evade % and attacker Hit Rate %.  Underflowed (i.e. 32767 to
   65535) Hit Rate % gets treated as zero here, which won't let you hit anything.
   Underflowing it below 32767 will get it capped at a nice 99.  Meanwhile, no real capping
   of your Evade stat is done in the process; it's assumed that ((Evade + 1) DIV 4) will
   range from 0 through 25.  So if it's too high, there's a good likelihood the hit chance
   will be retrieved from gibberish.

   A character's base Hit Rate is never higher than his base Evade, so you won't be able to
   underflow the latter without also underflowing (and probably effectively zeroing) the
   former.  Thus, there's next to no point to using the negative wrapping with Speed.

   However, it's theoretically possible to repeat the stat underflow via dying bug tons of
   times (at the low end, high 60s for Poison), and wind up with a Hit Rate that's ~32866 or
   less, and gets capped at 99 by the character's stat calculation function.  That's
   alongside an Evade that is a little over 32866, thus left uncapped by said function, and
   can possibly point to advantageous data when used by the Chance to Hit function.  You'd
   need to be willing to study Function 8F/BA06, do some math to figure out where the
   pointer winds up, and then if the pointer is 8000h to FFFFh, study the relevant ROM
   addresses in Bank 8F (if it's 0 to 7FFFh, don't even bother).  And then be prepared to do
   it all over again whenever your character levels up. :P  IOW, don't bother.


Q: Why doesn't the game just zero the damned statistic modifiers upon death, instead of
   subtracting from them?

A: Since status "clean up" functions are being called anyway to reverse various flags (like
   Barrier's invincibility) when dying, it's sensible enough to employ their statistic boost
   subtraction then as well.

   As for WHY these functions subtract rather than simply zeroing, I believe it's not
   outright necessary in the vanilla game, but would be so in a hypothetical expansion
   of it that allowed two separate things (spells/items/etc.) to alter the same stat.
   (Pixie Dust and Regenerate wouldn't count as separate, because they use a single status.)
   Some background follows.

   As Section 0 described, there are actually two variables involved here.  One can best
   be called "Current boost designated for relevant statistic(s) by Status X", and another
   "Overall boost to Statistic Y from alchemy/item statuses".  (btw, the former is properly
   retained through a save and reload, while the latter isn't.)  For brevity, I'll refer to
   them as Designated_Boost_Status_X and Total_Boost_Statistic_Y, respectively.  They work
   like this:

   A) When Status X is removed (including from expiration):
     - Subtract Designated_Boost_Status_X from Total_Boost_Statistic_Y, for any
       pertinent statistics.
     - Clear the status, zero its timer, and zero out Designated_Boost_Status_X.

   B) When giving Status X to a target from an alchemy casting:
     - Check if target already has the status.  If so, clean up and remove it
       per (A).
     - Give the status, and set its timer and Designated_Boost_Status_X to zero.
     - Set Designated_Boost_Status_X to a value based on the current spell
       level and some randomization.
     - For any relevant statistics, add Designated_Boost_Status_X to
       Total_Boost_Statistic_Y.

   That seems overly complex, but it actually gives the game the ability to
   have multiple separate statuses/sources modifying a statistic at one time.
   Even though I don't think the game makes use of this robustness, it's
   something I want to keep.

   Simply zeroing "Overall boost to Statistic Y from alchemy/item statuses"
   inside of the clean up function of a relevant status, which is run whenever
   that status expires or is removed, would take away the capability.  Now, you
   COULD zero it near the end of Function 91/BB72 (or in 91/BB27 after the call
   returns), in a death-specific context, and avoid the issue.

   Ninakoru's Balance Patch 1.1 does the zeroing inside the cleanup functions,
   probably to tackle this bug and limit the effects of the "Unsaved stat
   boosts" one.  As mentioned, that reduces flexibility some, but shouldn't be
   cause for alarm, unless you're applying a fictional hack on top of his.


Q: This bug messes with the status slot pointer.  So why doesn't it botch the
   clearing of the status timer(s) and "Current boost designated for relevant
   statistic(s) by Status X" upon death?  After all, these variables are
   generally accessed with the pointer in the status timer handler code.

A: It's weird.  The handler actually relies on another function to clear the
   status, zero its main timer, and zero its "Time since/until last/next
   damage/healing interval" or "Current boost designated for relevant
   statistic(s) by Status X" variable.  This function is passed the Status ID
   as an argument, and it finds the correct 1-4 status slot on its own WITHOUT
   EVEN USING the faulty pointer.  So those variables are cleared fine.  (Note
   that this function clears the same three variables in a status slot no
   matter what, having no awareness of the specific status, so the handler
   still needs to perform some of the "clean up" steps.)

____________________________________________________________________________________________

5. REVISION HISTORY
____________________________________________________________________________________________

Version 0.20 : July 9, 2013; August 12, 22, 25, and 31, 2013; November 14-16, 18 or 19,
               21, and 28, 2013; December 7-9, 2013; January 4, 2014; February 3-4, 2014;
               April 19, 22, 23, 25, and 27, 2014; May 26 and 28, 2014; June 22, 28, and 30,
               2014; July 5-6, 2014; circa April 4, 2015; August 5, 7, and 8, 2015;
               March 20, 2016; July 14, 2016; August 9-11, 13 or 14 - 16, 18, 24, 26, 27,
               30, and 31, 2016; January 12, 2017; February 2-5, and 26, 2017; March 12-14,
               early 20s and 24, 2017; April 28, 2017:

  - late January 2013: MetaSigma discovers the bug.  He dryly comments, "I do believe
    something is amiss," before taking a sip of brandy, and falling asleep in his leather
    recliner.  After mustering up enough energy, he posts about it on SpeedDemosArchive.com
    on the 29th.  The variant relayed is a game-breaking one, where Poison is in the first
    slot, and 65xxx Attack for the Boy results.
  - July 9, 2013: Wrote good chunk of code for USA fix.
  - August 12, 2013: ninakoru posts on GameFAQs about getting the bug with the Dog, in a
    variant where statistic boosts are retained.
  - August 22, 25, and 31, 2013: Commented original code, fixed an instruction in patch
    code.
  - November 14 (and/through) 16, 18 or 19, 21, and 28, 2013: Wrote much of the bug and
    patch description that would appear in Readme.
  - December 7-9, 2013: Dumped and commented Plague, Corrosion, Slow Burn, Poison, and
    Regrowth status handlers.  Worked first four statuses into bug description.  Commented
    the fix code, turned into .asm file, and researched and incorporated European versions'
    differences (they all match each other, mildly different from USA).
  - January 4, 2014: Nitrodon explains why underflowing Speed is almost entirely pointless,
    which'll make it into the FAQ section.
  - February 3-4, 2014: TheAngryPanda questions about using either Atlas bug (i.e. this or
    the saving and reloading one) to boost Hit Rate and Evade rather than underflowing them.
    MetaSigma points out that this one can do it -- more material for the FAQ section.
  - Two+ months: Frustration with not being able to further condense the webpage
    description, knowing this Readme (mainly the FAQ section) will be big and take awhile to
    complete, and dealing with tax filing crap, pushes off project.
  - April 19, 22, 23, 25, and 27, 2014: Got rolling on FAQ section, which involved a good
    amount of copying+pasting from "Unsaved stat boosts fix" Readme.  Did some more
    commenting and tidying in code files.  Saved a byte (and let 91/BB72 keep its original
    starting address) in fix code.  Made USA patch and anti-patch, and first round of 
    testing on Boy seemingly goes okay.  Second round shows me that's dead-wrong, as I
    forgot to uncomment an "org" in .asm file. :/  Corrected .asm file, made new patch and
    anti-patch, and Boy and Dog testing both work for real.
  - May 26 and 28, 2014: Bug description had faulty examples where having Atlas, Defend, or
    Speed in the 1st status slot before dying would let its boost be subtracted from
    statistics altered by another of these statuses, or where having Regrowth in the 1st
    slot would let its secondary timer be subtracted.  Nope, just zero is subtracted in all
    four cases, which I realized from looking at code.  Rewrote much of main description,
    and gave Regrowth an entry in FAQ section to distinguish it from the periodic damagers.
    On flipside, added comments to code for Poison, Slow Burn, Corrosion, and Plague --
    which are the TRUE "special cases".  Fortunately, neither of these misconceptions
    stopped my patch from working correctly. :P  Weird how explaining things can become
    tougher than fixing them.
  - June 22, 28, and 30, 2014: Came up with Phil Collins quote, patch title, and worked more
    on description.
  - July 5-6, 2014: Improved Readme FAQ answer (concerning zeroing statistic boosts on
    death), worked on Readme header, corrected a comment in code.
  - 13 months: Almost 100% bupkus.
  - August 5, 7, and 8, 2015: Made some progress on webpage bug description, typing a couple
    paragraphs.  (Might've done a little in early March, too.)  Did most of the last FAQ
    entry in Readme; first came up with around April 4th.
  - 7.5 months: Nothing!  So a working USA patch has been collecting dust for 23 months now.
    Brilliant!
  - March 20, 2016; July 14; 2016: Readme FAQ and webpage description work.  The latter
    might finally be arriving at an acceptable size!
  - August 9-11, 13 or 14 - 16, 18, 24, 26, 27, 30, and 31, 2016: Okay, NOW I'm getting
    serious again.  Confirmed bug exists on English European version with both Boy and Dog,
    made patch and anti-patch for this version from my already-existing code, and tested
    with both characters.  Various improvements to Readme, in main description and FAQ
    section.  ex- In latter, corrected how many times you'd need to repeat this bug with
    Poison to wrap the stat boost back to where it was; I'd been way too high.  Added
    Sections 1 and 2 to Readme, with 2 being heavily based on stuff I'd posted on
    Datacrystal.  Deciphered "most recent" status flag, and added to Section 2.  Corrected
    and clarified a handful of things in original code comments, some of them already done
    in fix code comments over 2 years ago.  Realized that Status ID 0098h is Wings helper
    status, and added to list in Readme Section 2.  In FAQ entry #5, corrected "will get it
    capped at a nice 100" to 99.
  - 4.5 months: After such an active August, why not return to thumb-sitting?
  - January 12, 2017: Improved some comments in both original and patch code, mainly at
    91/BB89.
  - February 2-5, and 26, 2017: Added providers of 4 Call Bead spells in Section 2 (already
    had 3), minor bug description work, typed up / copy+pasted this giant revision history
    from even more monstrous notes file, and added "temporarily remove the other patch" to
    follow-up answer to FAQ entry #3.
  - March 12-14, early 20s and 24, 2017: Some comment improvement in both .asm files, a
    little more Readme work.  Condensed steps for booster statuses under 2nd to last FAQ
    entry.
  - end March through April 19, 2017: Sidetracked with preparing my taxes, though several
    days where I just lazed.  This marks yet another year of underperforming the market
    (albeit minorly this time, unlike 2013), while having taxes considerably longer and
    more complicated than if I'd just owned an index fund.  (Go long International Paper,
    Domtar, HP Inc., and Brother Industries?)
  - April 28, 2017: Finally released the damn patch!  Should've done so 3-4 weeks ago.  For
    that matter, the 3-YEAR wait since having a working USA patch is ridiculous, and was
    largely driven by struggling to shrink the webpage description ... by an eventual two
    lines.  However, documentation definitely benefitted from the delay, with this Readme
    and other files seeing several corrections and numerous improvements.

____________________________________________________________________________________________

6. CREDITS
____________________________________________________________________________________________

  - MetaSigma for discovering the bug and posting it on SpeedDemosArchive.com.  Also,
    "#W o L F T e a M", for whatever their role was.  And later for correcting me that Hit
    Rate and Evade can be boosted by the lingering stat boosts form of this bug.

  - ninakoru for posting about the Dog variant of the bug, which doesn't have auto-revive as
    a requirement.  Check out the fine "Secret of Evermore Balance Patch" (and "Hard Mode"
    variant) at http://www.ninakoru.com/

  - Nitrodon for explaining why underflowing Speed is almost entirely pointless.  I'd known
    it was somewhat pointless, but still thought there was a real use to it.

  - TheAngryPanda for asking about using either this bug or "Unsaved stat boosts" to raise
    Hit Rate and Evade more conventionally, rather than underflowing them.

____________________________________________________________________________________________

Secret of Evermore copyright 1995 Squaresoft.
This readme and all other files in the archive (as listed above)
  copyright 2013-2017 Assassin.
All rights reserved.
